﻿//
// Worker.cs: Abstract definition of a worker, which performs some job.
//
// Copyright 2010 Michael Goethe
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
//

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;

namespace S9PalCommon
{
    /// <summary>
    /// States that a worker can be in.
    /// </summary>
    public enum WorkerStatus
    {
        Active,         // Worker thread is running.
        Finished,       // Worker thread is finished.
        Paused,         // Worker thread is paused.
        Pausing,        // Worker thread has been issued a pause command, and is in the process of getting to a pause point.
        Stopped,        // Worker thread is stopped.
        Stopping,       // Worker thread has been issued a stop command, and is in the process of getting to a stop point.
    }

    /// <summary>
    /// Abstract definition of a worker, which performs some sort of job.
    /// </summary>
    public abstract class Worker
    {
        #region Fields

        // Events
        public event EventHandler ItemCategoryChanged;              // Raised when the ItemCategory property is changed.
        public event EventHandler ProgressChanged;                  // Raised when the Progress property is changed.
        public event EventHandler StatusChanged;                    // Raised when the Status property is changed.

        // Variables
        private String currentItemName = "Item Name";
        private int currentItemNumber = 0;
        private Image icon = null;
        private String itemCategory = "Item";
        private int itemCount = 0;
        private String name = "Untitled Job";
        private float progress = 0;
        private WorkerStatus workerStatus = WorkerStatus.Stopped;

        #endregion

        #region Properties

        /// <summary>
        /// Name of the item currently being processed.
        /// </summary>
        public String CurrentItemName
        {
            get
            {
                return currentItemName;
            }
            protected set
            {
                currentItemName = value;
            }

        }

        /// <summary>
        /// Index of the item currently being processed.
        /// </summary>
        public int CurrentItemNumber
        {
            get
            {
                return currentItemNumber;
            }
            protected set
            {
                currentItemNumber = value;
                Progress = (float)currentItemNumber / itemCount;
            }
        }

        /// <summary>
        /// Icon to display next to the worker in the menu.
        /// </summary>
        public Image Icon
        {
            get
            {
                return icon;
            }
            set
            {
                icon = value;
            }
        }

        /// <summary>
        /// Category of the items being processed.
        /// </summary>
        public String ItemCategory
        {
            get
            {
                return itemCategory;
            }
            protected set
            {
                if (itemCategory.CompareTo(value) != 0)
                {
                    itemCategory = value;
                    OnItemCategoryChanged();
                }
            }

        }

        /// <summary>
        /// Total number of items being processed.
        /// </summary>
        public int ItemCount
        {
            get
            {
                return itemCount;
            }
            protected set
            {
                itemCount = value;
            }
        }

        /// <summary>
        /// Name of the worker
        /// </summary>
        public String Name
        {
            get
            {
                return name;
            }
            set
            {
                name = value;
            }
        }

        /// <summary>
        /// Progress value of the job.
        /// </summary>
        public float Progress
        {
            get
            {
                return progress;
            }
            protected set
            {
                if (progress != value)
                {
                    progress = value;
                    OnProgressChanged();
                }
            }
        }

        /// <summary>
        /// Worker's status (active, paused, etc).
        /// </summary>
        public WorkerStatus Status
        {
            get
            {
                return workerStatus;
            }
            protected set
            {
                if (workerStatus != value)
                {
                    workerStatus = value;
                    OnStatusChanged();
                }
            }
        }

        #endregion

        #region Public Methods

        /// <summary>
        /// Pause the worker.
        /// </summary>
        public void Pause()
        {
            // Change the work status to "Pausing." It's up to the derived worker to wrap things up
            // and bring the worker to a status of "Paused."
            Status = WorkerStatus.Pausing;
        }

        /// <summary>
        /// Start the worker.
        /// </summary>
        public void Start()
        {
            // If the worker is already active, then don't start a new thread.
            if (Status == WorkerStatus.Active) 
                return;

            // Set the worker to "Active."
            Status = WorkerStatus.Active;

            // Fire up a new thread to perform the job.
            ThreadStart threadStarter = delegate { WorkThreadMethod(); };
            Thread thread = new Thread(threadStarter);
            thread.IsBackground = true;
            workerStatus = WorkerStatus.Active;
            thread.Start();
        }

        /// <summary>
        /// Stop the worker.
        /// </summary>
        public void Stop()
        {
            // Change the work status to "Stopping." It's up to the derived worker to wrap things up
            // and bring the worker to a status of "Stopped."
            Status = WorkerStatus.Stopping;
        }

        #endregion

        #region Protected Methods

        /// <summary>
        /// Called when the ItemCategory is changed.
        /// </summary>
        protected void OnItemCategoryChanged()
        {
            // Raise the ItemCategoryChanged event.
            EventHandler handler = ItemCategoryChanged;
            if (handler != null)
                handler(this, new EventArgs());
        }

        /// <summary>
        /// Called when the Progress is changed.
        /// </summary>
        protected void OnProgressChanged()
        {
            // Raise the ProgressChanged event.
            EventHandler handler = ProgressChanged;
            if (handler != null)
                handler(this, new EventArgs());
        }

        /// <summary>
        /// Entry point for the worker.
        /// </summary>
        protected abstract void OnStart();

        /// <summary>
        /// Called when the Status is changed.
        /// </summary>
        protected void OnStatusChanged()
        {
            // Raise the StatusChanged event.
            EventHandler handler = StatusChanged;
            if (handler != null)
                handler(this, new EventArgs());
        }

        /// <summary>
        ///  Invoked at the start of the job.
        /// </summary>
        protected virtual void WorkThreadMethod()
        {
#if (!DEBUG)
                try
                {
#endif
                    // Call the job's entry point.
                    OnStart();

                    // If the worker is still set to Active, change it's status to Finished.
                    if (Status == WorkerStatus.Active)
                        Status = WorkerStatus.Finished;
#if (!DEBUG)
                }
                catch (Exception ex)
                {
                    Global.Logger.Fatal(ex.ToString());
                    MessageBox.Show(ex.Message.ToString(), ex.GetType().ToString(), MessageBoxButtons.OK, MessageBoxIcon.Error);
                    throw ex;
                }
#endif
        }

        #endregion
    }
}
